-
Notifications
You must be signed in to change notification settings - Fork 462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor broadcasting for a needs of 4844 #5485
Conversation
skip blob txs when preparing msg with persistent txs add test for skipping blob txs when picking persistent txs to broadcast add tests for broadcasting local blob txs just after receiving it one more place to protect fix whitespaces connected my changes one more whitespace
There is a regression in hive eth protocol test (TestTransaction), need to debug it |
hive eth protocol 🟢 https://github.com/NethermindEth/nethermind/actions/runs/4498920158/jobs/7916126549 (no regression, same fail as on master) |
@@ -183,7 +182,18 @@ public virtual Task<byte[][]> GetNodeData(IReadOnlyList<Keccak> hashes, Cancella | |||
|
|||
public void SendNewTransaction(Transaction tx) | |||
{ | |||
SendMessage(new[] { tx }); | |||
if (tx.Hash != null && NotifiedTransactions.Set(tx.Hash)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QQ: we haven't had this if tx.Hash != null && NotifiedTransactions.Set(tx.Hash)
before. Is it fixing some bug, but not related to 4844?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is recently added feature #5449
It was changed for SendNewTransactions
:
https://github.com/NethermindEth/nethermind/pull/5449/files#diff-e2801b05e4ee2fda5592da789a82f0e4c733c8b20c8688020cbb0de71e3db5e5
and SendNewTransaction
was skipped. I just added it here too.
It is a protection from sending the same tx few times to one peer - we are caching sent transactions. Not directly related to 4844
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would move this to ShouldNotifyTransaction
method for this condition in both places.
|
||
public static int GetLength(this Transaction tx) | ||
{ | ||
return tx.GetLength(_transactionSizeCalculator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can remove GetLength
from transaction class and leave it as extension? And probably remove ITransactionSizeCalculator
interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why it wasn't working? This sounds like a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extension methods are static. In Transaction
we have _size
which is initialized as null
and updated after first call of tx.GetLength()
. If I move everything to extensions, all fields must be static. And _size
can't be static.
I can add public Size
in Transaction
and update it in extension method, but then it will be exposed and can be easily used wrong way (before updating to actual tx length value) - it doesn't seem as a good idea.
Potentially I can drop size
and then it can be fully in extensions, but length would be calculated after every call of tx.GetLength()
, not only in first one. That doesn't seem as a good idea as well
@@ -183,7 +182,18 @@ public virtual Task<byte[][]> GetNodeData(IReadOnlyList<Keccak> hashes, Cancella | |||
|
|||
public void SendNewTransaction(Transaction tx) | |||
{ | |||
SendMessage(new[] { tx }); | |||
if (tx.Hash != null && NotifiedTransactions.Set(tx.Hash)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would move this to ShouldNotifyTransaction
method for this condition in both places.
@@ -218,7 +228,7 @@ protected virtual void SendNewTransactionsCore(IEnumerable<Transaction> txs, boo | |||
packetSizeLeft = TransactionsMessage.MaxPacketSize; | |||
} | |||
|
|||
if (tx.Hash is not null) | |||
if (tx.Hash is not null && tx.Type != TxType.Blob) //additional protection from sending full blob-type txs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't there tx.SupportsBlobs
that would be better for future as an abstraction than just checking Type?
{ | ||
public static UInt256 CalculateGasPrice(this Transaction tx, bool eip1559Enabled, in UInt256 baseFee) | ||
private static readonly int MaxSizeOfTxForBroadcast = 128_000; //128KB, as proposed in https://eips.ethereum.org/EIPS/eip-5793 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wonder if they meant 128000 or 131072 bytes (1024*128)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually spec is talking about KB and KB = 1024B. Good catch. It's only proposed value and not strongly imposed, but I will change it - we will be more accurate
|
||
public static int GetLength(this Transaction tx) | ||
{ | ||
return tx.GetLength(_transactionSizeCalculator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why it wasn't working? This sounds like a good idea.
return tx.GetLength(_transactionSizeCalculator); | ||
} | ||
|
||
public static bool CanBeBroadcast(this Transaction tx) => tx.Type != TxType.Blob && tx.GetLength() <= MaxSizeOfTxForBroadcast; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tx.SupportsBlobs
List<Transaction>? persistentTxsToSend = null; | ||
List<Transaction>? persistentHashesToSend = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use ArrayPoolList's
? I think we could.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then we would need to declare it one method higher (in BroadcastPersistentTxs()
) and pass both to GetPersistentTxsToSend()
- otherwise it will be disposed too quick. We are calculating numberOfPersistentTxsToBroadcast
(so max size of collections) in GetPersistentTxsToSend()
- we would need to move it higher as well. It would be a bit ugly. And taking into consideration that
- this method is called once per block
- if there are no persistent txs (as in most of our nodes) right now we are not allocating anything - lists are
nulls
until there is an item eligible to add - using
ArrayPoolList
would be called no matter if we have eligible items
Actually it would be good to add basic check _persistentTxs.Count == 0
in the beginning and quick return if collection is empty. And then using ArrayPoolList
would have more sense. But then most of the logic from GetPersistentTxsToSend()
would be moved higher, to BroadcastPersistentTxs()
, so we could move everything and drop GetPersistentTxsToSend()
at all. And we will still need to do using ArrayPoolList
for both collections even if one would not have any items
I will add quick return in case of empty collection - it's a good idea. And about ArrayPoolList's
- if you want I can do further refactor, but I'm not convinced to that idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... Then instead of using ArrayPoolList, maybe we can have a List<> instance we can reuse? As this is single-threaded if I understand it correctly.
Changes
GetLength
method toTransactionExtensions
in TxPool projectThanks to that, there is no more need to pass
TxDecoder
as an argument inTransaction.GetLength(decoder)
(Core project doesn't have Rlp dependency).MaxSizeOfTxForBroadcast
=128KB
CanBeBroadcast
inTransactionExtensions
which is checking if tx is not blob-type and not larger thanMaxSizeOfTxForBroadcast
Eth68ProtocolHandler
which is dividing PersistentTxsToSend to full transactions (whichCanBeBroadcast
) and to tx hashes (which!CanBeBroadcast
, so would be only announced)SyncPeerProtocolHandlerBase
Types of changes
Testing
Requires testing
If yes, did you write tests?